package visiopuce.graph;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.entity.XYItemEntity;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.labels.XYToolTipGenerator;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.chart.renderer.xy.XYItemRendererState;
import org.jfree.data.xy.IntervalXYDataset;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.GradientPaintTransformer;
import org.jfree.ui.RectangleEdge;

public class FloatingBarItemRenderer extends XYBarRenderer {

	/** The default base value for the bars. */
	private double base;

	private XYFloatingBarDataset xyFloatingBarDataset;

	/**
	 * A flag that controls whether the bars use the y-interval supplied by the
	 * dataset.
	 */
	private boolean useYInterval;

	/** Percentage margin (to reduce the width of bars). */
	private double margin;

	/** A flag that controls whether or not bar outlines are drawn. */
	private boolean drawBarOutline;

	/**
	 * An optional class used to transform gradient paint objects to fit each
	 * bar.
	 */
	// private GradientPaintTransformer gradientPaintTransformer;
	/**
	 * The shape used to represent a bar in each legend item (this should never
	 * be <code>null</code>).
	 */
	private transient Shape legendBar;

	private BarToolTipGenerator generator;

	public Paint getItemPaint(int row, int column) {
		return xyFloatingBarDataset.getColor(row, column);
	}

	public Paint getItemFillPaint(int row, int column) {
		return xyFloatingBarDataset.getColor(row, column);

	}

	/**
	 * The default constructor.
	 */
	public FloatingBarItemRenderer(XYFloatingBarDataset xyFloatingBarDataset,
			BarToolTipGenerator generator) {
		this(0.0, xyFloatingBarDataset, generator);
	}

	/**
	 * Constructs a new renderer.
	 * 
	 * @param margin
	 *            the percentage amount to trim from the width of each bar.
	 */
	public FloatingBarItemRenderer(double margin,
			XYFloatingBarDataset xyFloatingBarDataset,
			BarToolTipGenerator generator) {
		super();
		this.margin = margin;
		this.base = 0.0;
		this.xyFloatingBarDataset = xyFloatingBarDataset;
		this.useYInterval = false;
		this.drawBarOutline = true;
		this.legendBar = new Rectangle2D.Double(-3.0, -5.0, 6.0, 10.0);
		this.generator = generator;
	}

	/**
	 * Returns the base value for the bars.
	 * 
	 * @return The base value for the bars.
	 */
	public double getBase() {
		return this.base;
	}

	/**
	 * Sets the base value for the bars and sends a {@link RendererChangeEvent}
	 * to all registered listeners. The base value is not used if the dataset's
	 * y-interval is being used to determine the bar length.
	 * 
	 * @param base
	 *            the new base value.
	 */
	public void setBase(double base) {
		this.base = base;
		notifyListeners(new RendererChangeEvent(this));
	}

	/**
	 * Returns a flag that determines whether the y-interval from the dataset is
	 * used to calculate the length of each bar.
	 * 
	 * @return A boolean.
	 */
	public boolean getUseYInterval() {
		return this.useYInterval;
	}

	/**
	 * Sets the flag that determines whether the y-interval from the dataset is
	 * used to calculate the length of each bar, and sends a
	 * {@link RendererChangeEvent} to all registered listeners..
	 * 
	 * @param use
	 *            the flag.
	 */
	public void setUseYInterval(boolean use) {
		this.useYInterval = use;
		notifyListeners(new RendererChangeEvent(this));
	}

	/**
	 * Returns the margin which is a percentage amount by which the bars are
	 * trimmed.
	 * 
	 * @return The margin.
	 */
	public double getMargin() {
		return this.margin;
	}

	/**
	 * Sets the percentage amount by which the bars are trimmed and sends a
	 * {@link RendererChangeEvent} to all registered listeners.
	 * 
	 * @param margin
	 *            the new margin.
	 */
	public void setMargin(double margin) {
		this.margin = margin;
		notifyListeners(new RendererChangeEvent(this));
	}

	/**
	 * Returns a flag that controls whether or not bar outlines are drawn.
	 * 
	 * @return A boolean.
	 */
	public boolean isDrawBarOutline() {
		return this.drawBarOutline;
	}

	/**
	 * Sets the flag that controls whether or not bar outlines are drawn and
	 * sends a {@link RendererChangeEvent} to all registered listeners.
	 * 
	 * @param draw
	 *            the flag.
	 */
	public void setDrawBarOutline(boolean draw) {
		this.drawBarOutline = draw;
		notifyListeners(new RendererChangeEvent(this));
	}

	/**
	 * Returns the gradient paint transformer (an object used to transform
	 * gradient paint objects to fit each bar.
	 * 
	 * @return A transformer (<code>null</code> possible).
	 */
	public GradientPaintTransformer getGradientPaintTransformer() {
		return null;
	}

	/**
	 * Returns the shape used to represent bars in each legend item.
	 * 
	 * @return The shape used to represent bars in each legend item (never
	 *         <code>null</code>).
	 */
	public Shape getLegendBar() {
		return this.legendBar;
	}

	/**
	 * Sets the shape used to represent bars in each legend item.
	 * 
	 * @param bar
	 *            the bar shape (<code>null</code> not permitted).
	 */
	public void setLegendBar(Shape bar) {
		if (bar == null) {
			throw new IllegalArgumentException("Null 'bar' argument.");
		}
		this.legendBar = bar;
		notifyListeners(new RendererChangeEvent(this));
	}

	/**
	 * Draws the visual representation of a single data item.
	 * 
	 * @param g2
	 *            the graphics device.
	 * @param state
	 *            the renderer state.
	 * @param dataArea
	 *            the area within which the plot is being drawn.
	 * @param info
	 *            collects information about the drawing.
	 * @param plot
	 *            the plot (can be used to obtain standard color information
	 *            etc).
	 * @param domainAxis
	 *            the domain axis.
	 * @param rangeAxis
	 *            the range axis.
	 * @param dataset
	 *            the dataset.
	 * @param series
	 *            the series index (zero-based).
	 * @param item
	 *            the item index (zero-based).
	 * @param crosshairState
	 *            crosshair information for the plot (<code>null</code>
	 *            permitted).
	 * @param pass
	 *            the pass index.
	 */
	public void drawItem(Graphics2D g2, XYItemRendererState state,
			Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot,
			ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset,
			int series, int item, CrosshairState crosshairState, int pass) {

		if (!getItemVisible(series, item)) {
			return;
		}
		IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset;

		double value0;
		double value1;
		if (this.useYInterval) {
			value0 = intervalDataset.getStartYValue(series, item);
			value1 = intervalDataset.getEndYValue(series, item);
		} else {
			value0 = this.base;
			value1 = intervalDataset.getYValue(series, item);
		}
		if (Double.isNaN(value0) || Double.isNaN(value1)) {
			return;
		}

		double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea,
				plot.getRangeAxisEdge());
		double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea,
				plot.getRangeAxisEdge());

		RectangleEdge location = plot.getDomainAxisEdge();
		Number startXNumber = intervalDataset.getStartX(series, item);
		if (startXNumber == null) {
			return;
		}
		double translatedStartX = domainAxis.valueToJava2D(startXNumber
				.doubleValue(), dataArea, location);
		translatedStartX = Math.max(translatedStartX, dataArea.getMinX());

		Number endXNumber = intervalDataset.getEndX(series, item);
		if (endXNumber == null) {
			return;
		}
		double translatedEndX = domainAxis.valueToJava2D(endXNumber
				.doubleValue(), dataArea, location);
		translatedEndX = Math.min(translatedEndX, dataArea.getMaxX());

		if (translatedStartX > dataArea.getMaxX()
				|| translatedEndX < dataArea.getMinX()) {
			return;
		}

		double translatedWidth = translatedEndX - translatedStartX;

		double translatedHeight = Math.abs(translatedValue1 - translatedValue0);

		if (getMargin() > 0.0) {
			double cut = translatedWidth * getMargin();
			translatedWidth = translatedWidth - cut;
			translatedStartX = translatedStartX + cut / 2;
		}

		Rectangle2D bar = null;
		PlotOrientation orientation = plot.getOrientation();
		if (orientation == PlotOrientation.HORIZONTAL) {
			bar = new Rectangle2D.Double(Math.min(translatedValue0,
					translatedValue1), Math.min(translatedStartX,
					translatedEndX), translatedHeight, translatedWidth);
		} else if (orientation == PlotOrientation.VERTICAL) {
			bar = new Rectangle2D.Double(translatedStartX, Math.min(
					translatedValue0, translatedValue1), translatedWidth,
					translatedHeight);
		}

		Paint itemPaint = ((XYFloatingBarDataset) intervalDataset).getColor(
				series, item);

		g2.setPaint(itemPaint);
		g2.fill(bar);
		if (isDrawBarOutline()) {
			Stroke stroke = (BasicStroke) getItemOutlineStroke(series, item);
			Paint paint = ((XYFloatingBarDataset) intervalDataset)
					.getContourColor(series, item);
			if (stroke != null && paint != null) {
				Color cnColor = new Color(153, 51, 10);
				Color snpColor = new Color(153, 51, 204);
				if (paint.equals(Color.black)
						|| cnColor.getRGB() == ((Color) paint).getRGB()
						|| snpColor.getRGB() == ((Color) paint).getRGB()) {
					g2.setStroke(stroke);
				} else {
					g2.setStroke(new BasicStroke(3.0f));
				}
				g2.setPaint(paint);
				g2.draw(bar);
			}
		}

		// TODO: we need something better for the item labels
		if (isItemLabelVisible(series, item)) {
			drawItemLabel(g2, dataset, series, item, plot, generator, bar,
					value1 < 0.0);
			// drawItemLabel(g2, orientation, dataset, series, item, bar
			// .getCenterX(), bar.getY(), value1 < 0.0);
		}

		// add an entity for the item...
		if (info != null) {
			EntityCollection entities = info.getOwner().getEntityCollection();
			if (entities != null) {
				String tip = null;
				XYToolTipGenerator generator = getToolTipGenerator(series, item);
				if (generator != null) {
					tip = generator.generateToolTip(dataset, series, item);
				}
				String url = null;
				if (getURLGenerator() != null) {
					url = getURLGenerator().generateURL(dataset, series, item);
				}
				XYItemEntity entity = new XYItemEntity(bar, dataset, series,
						item, tip, url);
				entities.add(entity);
			}
		}

	}

	public Paint getItemLabelPaint(int row, int column) {
		Paint p = Color.black;
		try {
			p = this.xyFloatingBarDataset.getTexteColor(row, column);
		} catch (Exception e) {
			p = Color.black;
		}
		return p;
	}

}
